﻿/// <reference name="MicrosoftAjax.js"/>
Type.registerNamespace("AjaxSnippets");

AjaxSnippets.ValueExtractors = function() { };

AjaxSnippets.ValueExtractors.prototype = {
    fromAny: function(id) {
        return id;
    },
    fromTextBox: function(id) {
        return $get(id).value;
    },
    fromDropDown: function(id) {
        var el = $get(id);
        return el.options[el.selectedIndex].value;
    },
    fromRadioList: function(id) {
        var i, els = $get(id).getElementsByTagName('input');
        for (i = 0; i < els.length; i++) {
            if (els[i].checked) { return els[i].value; }
        }
        return null;
    },
    fromCheckList: function(id) {
        var i, els = $get(id).getElementsByTagName('input'), vals = new Array();
        for (i = 0; i < els.length; i++) {
            vals.push(els[i].checked);
        }
        return vals;
    },
    fromListBox: function(id) {
        var i, el = $get(id), vals = new Array();
        for (i = 0; i < el.options.length; i++) {
            if (el.options[i].selected) { vals.push(el.options[i].value); }
        }
        return vals;
    },
    fromCheckBox: function(id) {
        return $get(id).checked;
    }
};

AjaxSnippets.ValueExtractors.registerClass('AjaxSnippets.ValueExtractors');

AjaxSnippets._SnippetManager = function() {
    AjaxSnippets._SnippetManager.initializeBase(this);
    this._callbacks = {};
    this._loadedScriptUrls = new Array();
    this._loadedCssUrls = new Array();
    this._extractors = new AjaxSnippets.ValueExtractors();
    this._scriptsLoading = 0;
    this._functionStack = new Array();
    this._appNotifyScriptLoaded = Sys.Application.notifyScriptLoaded;
    Sys.Application.notifyScriptLoaded = Function.createDelegate(this, this._notifyScriptLoaded);
};

AjaxSnippets._SnippetManager.prototype = {
    initialize: function() {
        AjaxSnippets._SnippetManager.callBaseMethod(this, 'initialize');
        var i, scripts = document.getElementsByTagName("script"),
            css = document.getElementsByTagName("link");
        for (i = 0; i < scripts.length; i++) {
            if (scripts[i].src) {
                this._loadedScriptUrls.push(scripts[i].src);
            }
        }
        for (i = 0; i < css.length; i++) {
            if (css[i].href && css.type.toLower() == 'text/css') {
                this._loadedCssUrls.push(css[i].href);
            }
        }
    },
    _notifyScriptLoaded: function() {
        this._appNotifyScriptLoaded();
        this._scriptsLoading--;
        if (this._scriptsLoading <= 0) {
            this.execStackedFunctions();
            this._scriptsLoading = 0;
        }
    },
    registerCallback: function(name, servicePath, serviceMethod, componentId, beforeCallback, callbackSuccess, callbackFailed, mappings) {
        var callbackObj = (componentId === null) ? window : $find(componentId);
        if (typeof (callbackObj) === 'undefined') { throw Error.argumentUndefined('componentId', 'Component not found'); }
        if (!callbackSuccess) throw Error.argumentNull('callbackSuccess', 'cannot be null, you need at least a callback success handler');
        if (beforeCallback && typeof callbackObj[beforeCallback] === 'undefined') { throw Error.argumentUndefined('beforeCallback', 'Undefined function on designated callback object'); }
        if (typeof callbackObj[callbackSuccess] === 'undefined') { throw Error.argumentUndefined('callbackSuccess', 'Undefined function on designated callback object'); }
        if (callbackFailed && typeof callbackObj[callbackFailed] === 'undefined') { throw Error.argumentUndefined('callbackFailed', 'Undefined function on designated callback object'); }

        this._callbacks[name] = Function.createDelegate(this, function() {
            var i, callBackArgs = {};
            for (i = 0; i < mappings.length; i++) {
                callBackArgs[mappings[i].param] = this._extractors[mappings[i].func](mappings[i].id);
            }
            if (beforeCallback) { callbackObj[beforeCallback](); }
            Sys.Net.WebServiceProxy.invoke(
                servicePath,
                serviceMethod,
                false,
                callBackArgs,
                Function.createDelegate(callbackObj, callbackObj[callbackSuccess]),
                callbackFailed ? Function.createDelegate(callbackObj, callbackObj[callbackFailed]) : null
            );
        });
    },
    callback: function(name) {
        var callbackFunc = this._callbacks[name];
        if (typeof (callbackFunc) === 'undefined') {
            throw Error.argumentOutOfRange('name', name, 'SnippetManager: No callback was found with the given name');
        }
        else {
            callbackFunc();
        }
    },
    loadScript: function(url) {
        if (!this._arrayContains(url, this._loadedScriptUrls)) {
            this._scriptsLoading++;
            this._loadedScriptUrls.push(url);
            document.getElementsByTagName("head")[0].appendChild(
                this._createElement("script", { 'src': url, 'type': 'text/javascript' })
            );
        }
    },
    // TODO: Refactor this to loadLink
    loadCss: function(href) {
        if (!this._arrayContains(href, this._loadedCssUrls)) {
            this._loadedCssUrls.push(href);
            document.getElementsByTagName("head")[0].appendChild(
                this._createElement("link", { 'rel': 'stylesheet', 'type': 'text/css', 'href': href })
            );
        }
    },
    stackFunction: function(f) {
        if (this._scriptsLoading == 0) {
            f(); this.execStackedFunctions();
        }
        else {
            this._functionStack.push(f);
        }
    },
    _createElement: function(tag, ats) {
        var el = document.createElement(tag);
        for (var x in ats) {
            el.setAttribute(x, ats[x]);
        }
        return el;
    },
    _arrayContains: function(val, arr) {
        for (var i = 0, length = arr.length; i < length; i++) {
            if (arr[i] == val) return true;
        }

        return false;
    },
    removeComponent: function(id) {
        var c = $find(id);
        if (c) { Sys.Application.removeComponent(c); }
    },
    execStackedFunctions: function() {
        while (this._functionStack.length > 0) {
            var f = this._functionStack.pop()();
        }
    },
    renderSnippet: function(snippet, output) {
        if (this._scriptsLoading > 0 || this._functionStack.length > 0) {
            // if we are loading scripts delay rendering this snippet
            // FIXME: This is not nice?
            window.setTimeout(function() { AjaxSnippets.SnippetManager.renderSnippet(snippet, output) }, 200);
        }
        else {
            var i, r = new RegExp('<script[^>]*>([\\S\\s]*?)<\/script>', 'img'), matches = snippet.match(r);
            $get(output).innerHTML = snippet.replace(r, '');
            for (i = 0; i < matches.length; i++) {
                eval(matches[i].replace(r, '$1'));
            }
        }
    },
    dispose: function() {
        this._callbacks = null;
        this._extractors = null;
        AjaxSnippets._SnippetManager.callBaseMethod(this, 'dispose');
    }
};

AjaxSnippets._SnippetManager.registerClass('AjaxSnippets._SnippetManager', Sys.Component);

Sys.Application.add_init(function() {
    AjaxSnippets.SnippetManager = $create(AjaxSnippets._SnippetManager, { 'id': 'SnippetManager' });   
});